﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DDD {
    /// <summary>
    /// 全てのコンポーネントの基底となるクラス
    /// </summary>
    /// <remarks>
    /// ノードにアタッチ可能なコンポーネントは全てこのクラスを継承します。
    /// 単なる3D空間上の点に過ぎないノードはいろいろな機能を持ったコンポーネントをアタッチすることで、
    /// 多様な役割を果たすようになります。
    /// このクラスはまたノードとコンポーネントの橋渡しとメッセージ送信を行います。
    /// </remarks>
    public abstract class Component : Object {

        #region Field
        Node node;
        #endregion

        #region Constructor
        /// <summary>
        /// コンストラクター
        /// </summary>
        public Component () {
            this.node = null;
        }
        #endregion

        #region Property
        /// <summary>
        /// アタッチされたノード
        /// </summary>
        public Node Node {
            get { return node; }
        }

        /// <summary>
        /// このオブジェクトが参照する全DDDオブジェクトを列挙する列挙子
        /// </summary>
        /// <remarks>
        /// 参照にnullは含まれません。同一のオブジェクトが複数出現する可能性があります。
        /// </remarks>
        public override IEnumerable<Object> References {
            get {
                return base.References;
            }
        }
        #endregion

        #region Mehthod

        /// <summary>
        /// ノードをセットします
        /// </summary>
        /// <param name="node"></param>
        internal void SetNode (Node node) {
            this.node = node;
        }

        /// <summary>
        /// 指定のノードにメッセージを送信します
        /// </summary>
        /// <remarks>
        /// コンポーネントは任意のノードにメッセージを送信し、
        /// そのノードにアタッチされたコンポーネントの同名の関数を呼び出すことができます。
        /// これをメッセージパッシングと呼びます。
        /// メッセージの送信には<see cref="SendMessage"/>メソッドを使用します。
        /// このメソッドはいくつかある派生型の1つで、
        /// 宛先ノードの全てのコンポーネントの指定の関数を(もしあれば)全て呼び出します。
        /// 引数はありません。
        /// </remarks>
        /// <param name="node">宛先のノード</param>
        /// <param name="name">呼び出すメソッドの名前</param>
        public void SendMessage (Node node, string name) {
            if (node == null) {
                throw new ArgumentNullException ("Node is null");
            }
            node.RecvMessage (name);
        }

        /// <summary>
        /// 指定のノードにメッセージを送信します
        /// </summary>
        /// <remarks>
        /// コンポーネントは任意のノードにメッセージを送信し、
        /// そのノードにアタッチされたコンポーネントの同名の関数を呼び出すことができます。
        /// これをメッセージパッシングと呼びます。
        /// メッセージの送信には<see cref="SendMessage"/>メソッドを使用します。
        /// このメソッドはいくつかある派生型の1つで、
        /// 宛先ノードの全てのコンポーネントの指定の関数を(もしあれば)全て呼び出します。
        /// 引数を1つ取ります。
        /// </remarks>
        /// <typeparam name="TArg1">引数1の型</typeparam>
        /// <param name="node">宛先ノード</param>
        /// <param name="name">呼び出すメソッドの名前</param>
        /// <param name="arg1">引数1</param>
        public void SendMessage<TArg1> (Node node, string name, TArg1 arg1) {
            if (node == null) {
                throw new ArgumentNullException ("Node is null");
            }
            node.RecvMessage (name, arg1);
        }

        /// <summary>
        /// 指定のノードにメッセージを送信する
        /// </summary>
        /// <remarks>
        /// コンポーネントは任意のノードにメッセージを送信し、
        /// そのノードにアタッチされたコンポーネントの同名の関数を呼び出すことができます。
        /// これをメッセージパッシングと呼びます。
        /// メッセージの送信には<see cref="SendMessage"/>メソッドを使用します。
        /// このメソッドはいくつかある派生型の1つで、
        /// 宛先ノードの指定のコンポーネント型の指定の関数を(もしあれば)呼び出します。
        /// 引数はありません。
        /// </remarks>
        /// <typeparam name="TComp">コンポーネント型</typeparam>
        /// <param name="node">宛先ノード</param>
        /// <param name="name">呼び出すメソッドの名前</param>
        public void SendMessage<TComp> (Node node, string name) where TComp : Component {
            if (node == null) {
                throw new ArgumentNullException ("Node is null");
            }
            node.RecvMessage<TComp> (name);
        }


        /// <summary>
        /// 指定のノードにメッセージを送信する
        /// </summary>
        /// <remarks>
        /// コンポーネントは任意のノードにメッセージを送信し、
        /// そのノードにアタッチされたコンポーネントの同名の関数を呼び出すことができます。
        /// これをメッセージパッシングと呼びます。
        /// メッセージの送信には<see cref="SendMessage"/>メソッドを使用します。
        /// このメソッドはいくつかある派生型の1つで、
        /// 宛先ノードの指定のコンポーネント型の指定の関数を(もしあれば)呼び出します。
        /// 引数を1つ取ります。
        /// </remarks>
        /// <typeparam name="TComp">コンポーネント型</typeparam>
        /// <typeparam name="TArg1">引数1の型</typeparam>
        /// <param name="node">宛先ノード</param>
        /// <param name="name">呼び出すメソッドの名前</param>
        /// <param name="arg1">引数1</param>
        public void SendMessage<TComp, TArg1> (Node node, string name, TArg1 arg1) where TComp : Component {
            if (node == null) {
                throw new ArgumentNullException ("Node is null");
            }
            node.RecvMessage<TComp, TArg1> (name, arg1);
        }



        /// <summary>
        /// ゲームロジックのアップデート処理を行います
        /// </summary>
        /// <remarks>
        /// 派生型はこの仮想関数をオーバーライドして独自のアップデート処理を実装可能です(実装すべきです)。
        /// </remarks>
        /// <param name="time">ワールド時刻</param>
        public virtual void OnUpdate (int time) {
            // do nothing
        }

        /// <summary>
        /// Uniform変数の値を更新します
        /// </summary>
        /// <remarks>
        /// 派生型はこの仮想関数をオーバーライドして独自のリフレッシュ処理を実装可能です。
        /// 通常は標準のリフレッシュ処理で十分なのでこれを実装すべきではありません。
        /// </remarks>
        public virtual void OnReflesh () {
            throw new NotImplementedException ("Sorry");
        }


        /// <summary>
        /// コリジョンが発生したときに１回だけ呼ばれます
        /// </summary>
        /// <remarks>
        /// 派生型はこの仮想関数をオーバーライドして独自のコリジョン処理を実装可能です。
        /// コリジョン検出は同じノードに<see cref="CollisionVolume"/>コンポーネントがアタッチされていて、
        /// <see cref="Physics3D.Collide"/>メソッドが呼ばれると呼び出される可能性があります。
        /// <see cref="OnCollisionEnter"/>メソッドはコリジョン領域がオーバーラップした最初の1回だけ呼ばれます。
        /// </remarks>
        /// <param name="collisions">発生したコリジョン情報</param>
        public virtual void OnCollisionEnter (Collision[] collisions) {
            // do nothing
        }

        /// <summary>
        /// コリジョンが解消したときに1回だけ呼ばれます
        /// </summary>
        /// <remarks>
        /// このメソッドは原則<see cref="OnCollisionEnter"/>と同じですが、
        /// コリジョン領域のオーバーラップが解消された最初の1回だけ呼ばれます。
        /// </remarks>
        /// <param name="collisions">発生したコリジョン情報</param>
        public virtual void OnCollisionExit (Collision[] collisions) {
            // do nothing
        }

        /// <summary>
        /// 現在のオブジェクトの状態を表す文字列を返します。
        /// </summary> 
        public override string ToString () {
            return string.Format ("");
        }

        #endregion
    }
}
